#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h> // For atoi function
#include <pthread.h> // For pthreads

#include "mosquitto.h"
#include "cJSON.h"
#include "linux/can.h"
#include "can.h"
#include "modbus_poll.h"


#include "lvgl/lvgl.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_drivers/indev/evdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>

#include "gui_guider.h"
#include "events_init.h"
#include "ui.h"
#include "mqtt.h"
#include "logger.h"
#include "conf.h"

#define  CAN_AGREEMENT 	      "can0"
#define DISP_BUF_SIZE 	      (128 * 1024)
#define HORIZONTAL_RESOLUTION 800  
#define VERTICAL_RESOLUTION   480


#define SERIAL 		"/dev/ttymxc3"
#define BAUD   		 115200
#define SERIAL_DATA_BITS 8
#define SERIAL_CRC       1
#define REGISTER_ADDRESS 0
#define REGISTER_NUMBER  2


void mqtt_clean(struct mosquitto *mosq);  //MQTT Clean
static void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg); //parse subscription message formats

void *mqtt_subscribe(void *arg);  //mqtt subscribe
void *mqtt_publish(void *arg);    //mqtt publish
void *lcd_timer_handle(void *arg);
void *mosq_reconnect(void *arg);


typedef struct
{
	struct	mosquitto *mosq;
        int	can_fd;	
	int	mosq_connect_flag;
}can_mosq;

int main(int argc, char **argv)
{
	pthread_t 	sub_thread;
	pthread_t	pub_thread;
	pthread_t 	mqtt_reconnect_thread;
	pthread_t	lcd_thread;  //Define the pthread handle

	int 		ret;
	char            *logfile="modbus_can.log";
        int             loglevel=LOG_LEVEL_INFO;
        int             logsize=10;

	static 		lv_color_t buf[DISP_BUF_SIZE];
	static 		lv_disp_draw_buf_t disp_buf;
	static 		lv_indev_drv_t indev_drv_1;
	static 		lv_disp_drv_t disp_drv;

	const char 	*interface_name = CAN_AGREEMENT;	//Specify can interface
	
	can_mosq can_mosq_fd;
	
	can_mosq_fd.mosq_connect_flag = 0;
	
	if( log_open(logfile, loglevel, logsize, THREAD_LOCK_NONE) < 0 )
        {
                fprintf(stderr, "Initial log system failed\n");
                return -2;
        }

        log_debug("program start running.\n");

 	//mqtt_configuration(); //configuration files

	mosquitto_lib_init();  //mosquitto initialize

	//mosq = mosquitto_new(conn_para.clientId, true, NULL); //create mosquitto handle
	can_mosq_fd.mosq = mosquitto_new(conn_para.clientId, true, NULL);
	if (mosquitto_username_pw_set(can_mosq_fd.mosq, conn_para.username, conn_para.password) != MOSQ_ERR_SUCCESS)//Set connection parameters
	{
		log_error("set connection parametes failure: %s\n", strerror(errno));
		goto cleanup;
	}

	printf("mqtt connect parameters set OK\n");

	if (mosquitto_connect(can_mosq_fd.mosq, conn_para.hostname, conn_para.port, conn_para.keepalive) != MOSQ_ERR_SUCCESS)
		//mosquitto connect function
	{
		log_error("connect server failure: %s\n", strerror(errno));
		can_mosq_fd.mosq_connect_flag = 1;
	}
	printf("connect ok\n");

	mosquitto_message_callback_set(can_mosq_fd.mosq, my_message_callback);	//Set callback function

	lv_init();
	fbdev_init();


	lv_disp_draw_buf_init(&disp_buf, buf, NULL, DISP_BUF_SIZE);

	lv_disp_drv_init(&disp_drv);
	disp_drv.draw_buf   = &disp_buf;
	disp_drv.flush_cb   = fbdev_flush;
	disp_drv.hor_res    = HORIZONTAL_RESOLUTION ;
	disp_drv.ver_res    = VERTICAL_RESOLUTION;
	lv_disp_drv_register(&disp_drv);

	evdev_init();
	lv_indev_drv_init(&indev_drv_1); /*Basic initialization*/
	indev_drv_1.type = LV_INDEV_TYPE_POINTER;

	/*This function will be called periodically (by the library) to get the mouse position and state*/
	indev_drv_1.read_cb = evdev_read;
	lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv_1);

	ui_init();
	can_mosq_fd.can_fd = can_set_controller(CAN_AGREEMENT); //can

	// Create thread for subscribing
	ret = pthread_create(&sub_thread, NULL, mqtt_subscribe, &can_mosq_fd);  
	if (ret != 0) {
		log_error("Failed to create subscribe thread: %s\n", strerror(ret));
		goto cleanup;
	}

	// Create thread for publishing
	ret = pthread_create(&pub_thread, NULL, mqtt_publish, &can_mosq_fd);
	if (ret != 0) 
	{ 
		log_error("Failed to create publish thread: %s\n", strerror(ret));
		goto cleanup;
	}
	ret = pthread_create(&lcd_thread, NULL, lcd_timer_handle, NULL);
	if(ret != 0)
	{
		log_error("lcd lv_timer handler thread failure: %s\n", strerror(ret));
	}
	
	ret = pthread_create(&mqtt_reconnect_thread, NULL, mosq_reconnect,&can_mosq_fd);
	if (ret != 0)
	 {
	 	 log_error("Failed to create mqtt_reconnect thread: %s\n", strerror(ret));
	 }
	
	// Join threads (wait for them to finish)
	pthread_join(sub_thread, NULL);
	pthread_join(pub_thread, NULL);
	pthread_join(lcd_thread, NULL);
	pthread_join(mqtt_reconnect_thread, NULL);
cleanup:
	mqtt_clean(can_mosq_fd.mosq);
	return 0;
}


void mqtt_clean(struct mosquitto *mosq)
{
	mosquitto_destroy(mosq);
	mosquitto_lib_cleanup();
}

static void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)
{
	can_mosq can_mosq_fd;
	char  buf[200] = {0};
	struct can_frame frame;			//can Message struct


	cJSON *root = cJSON_Parse((char *)msg->payload); //Para the cJson payload in MQTT message 
	if (root == NULL)
	{
		log_error("JSON parsing error\n");
		return;
	}

	cJSON *method = cJSON_GetObjectItem(root, "method");  //Get the "method" field from the JSON root object
	if (method != NULL && cJSON_IsString(method))  //if the "method" field exists and is of string type
	{
		if (strcmp(method->valuestring, "thing.service.property.set") == 0)   //
		{
			cJSON *params = cJSON_GetObjectItem(root, "params");
			if (params != NULL)
			{
				cJSON *can_id = cJSON_GetObjectItem(params, "can_id");
				cJSON *can_dlc = cJSON_GetObjectItem(params, "can_dlc");
				cJSON *can_data0 = cJSON_GetObjectItem(params, "can_data0");
				cJSON *can_data1 = cJSON_GetObjectItem(params, "can_data1");
				cJSON *can_data2 = cJSON_GetObjectItem(params, "can_data2");
				cJSON *can_data3 = cJSON_GetObjectItem(params, "can_data3");
				cJSON *can_data4 = cJSON_GetObjectItem(params, "can_data4");
				cJSON *can_data5 = cJSON_GetObjectItem(params, "can_data5");
				cJSON *can_data6 = cJSON_GetObjectItem(params, "can_data6");
				cJSON *can_data7 = cJSON_GetObjectItem(params, "can_data7");

				if (cJSON_IsNumber(can_id) && cJSON_IsNumber(can_dlc))
				{
					int id = can_id->valueint;
					int dlc = can_dlc->valueint;
					frame.can_id = id;
					frame.can_dlc = dlc;

					// Make sure dlc is within valid range (0 to 8 as per CAN standard)
					if (dlc >= 0 && dlc <= 8)
					{
						// Array to store can data
						double can_data[8] = {0}; // Initialize with zeros

						// Read can data into the array
						cJSON *can_data_items[8] = {can_data0, can_data1, can_data2, can_data3,
							can_data4, can_data5, can_data6, can_data7};

						for (int i = 0; i < dlc; ++i)
						{
							if (cJSON_IsNumber(can_data_items[i])) //Check whether the type is an integer
							{
								can_data[i] = can_data_items[i]->valuedouble;
								frame.data[i] = can_data_items[i]->valuedouble;
							}
						}

						// Print received CAN message
						printf("Receive CAN message: id=%d, dlc=%d, data=", id, dlc);
						int index = snprintf(buf,sizeof(buf),"CAN message: id=%d, dlc=%d ",id, dlc);
						for (int i = 0; i < dlc; i++)
						{
							printf("%.2f ", can_data[i]);
							index += snprintf(buf+index,sizeof(buf),"data[%d]: %.2f",i,can_data[i]); 
						}
						printf("\n");
						if(write(can_mosq_fd.can_fd, &frame, sizeof(struct can_frame))!= sizeof(struct can_frame))
						{
							log_error("write failure: %s\n", strerror(errno));
							close(can_mosq_fd.can_fd);
						}
						printf("write %s\n", buf);
						memset(buf,0, sizeof(buf));
					}
					else
					{
						log_warn("Invalid DLC value: %d\n", dlc);
					}
				}
				else
				{
					log_warn("Invalid or missing CAN data fields\n");
				}
			}
			else
			{
				log_warn("Missing params object in message\n");
			}
		}
		else
		{
			   log_warn("Unsupported method: %s\n", method->valuestring);
		}
	}
	else
	{
		log_warn("Missing or invalid method field\n");
	}

	cJSON_Delete(root);
}



void *mqtt_subscribe(void *arg)
{
	can_mosq* can_mosq_fd = (can_mosq*)arg;
	int mid;
	conn_para_t *conn = &conn_para;

	if (mosquitto_subscribe(can_mosq_fd->mosq, &mid, conn->sub_topic, conn->qos) != MOSQ_ERR_SUCCESS) 
	{
		log_error("mosquitto subscribe failure: %s\n", strerror(errno));
		return NULL;
	}
	printf("subscribe OK\n");

	while (1)
       	{
		if (mosquitto_loop(can_mosq_fd->mosq, -1, 1) != MOSQ_ERR_SUCCESS) 
		{
			log_error("Mosquitto loop error\n");
			break;
		}
	}

	return NULL;
}

void *lcd_timer_handle(void *arg) //lcd运行
{
	while(1)
	{
		lv_timer_handler();
	}
}

void *mosq_reconnect(void *arg)
{
	can_mosq* mosq_connect = (can_mosq*)arg;

	while(1)
	{
		if(mosq_connect->mosq_connect_flag == 1)
		{
			if (mosquitto_connect(mosq_connect->mosq, conn_para.hostname, conn_para.port, conn_para.keepalive) != MOSQ_ERR_SUCCESS)
               		 //mosquitto connect function
		        {
                		log_error("connect server failure: %s\n", strerror(errno));
                		mosq_connect->mosq_connect_flag = 1;
        		}
			else 
			{
				mosq_connect->mosq_connect_flag = 0;
				pthread_exit(NULL);
			}

		}	
	}
}


void *mqtt_publish(void *arg)
{
	can_mosq*  can_mosq_fd = (can_mosq*)arg;
	modbus_t 	*ctx;
	int 		mid1;
	conn_para_t 	*conn = &conn_para;
	int 		modbus_connect_flag = 0;
	int		rc = 0;
	uint16_t	tab_reg[32];
	char 		buf[20];
	int		lux;
	int		noisy;
	int 		index = 0;
	pthread_mutex_t mqtt_mutex =  PTHREAD_MUTEX_INITIALIZER;

	struct can_frame received_frame;

	while (1)
       	{

		if(modbus_connect_flag == 0) //重连机制
		{
			if( (ctx = modbus_new_rtu(SERIAL, BAUD, 'N', SERIAL_DATA_BITS, SERIAL_CRC)) == NULL)
			{
				log_error("modbus_new_rtu failure\n");
			}	
			modbus_set_slave(ctx, 1);//从机端设置从机地址
			if(modbus_connect(ctx) == -1)
			{
				log_error("modbus_connect failure: %s\n", modbus_strerror(errno));
				//modbus_connect_flag = 0;
			}
			else
			{
				modbus_connect_flag = 1;
			}
		}

		rc = modbus_read_registers(ctx, REGISTER_ADDRESS, REGISTER_NUMBER, tab_reg);
		if(rc == -1)
		{
			log_error("modbus read failure: %s\n", modbus_strerror(errno));
			modbus_connect_flag = 0;
			modbus_close(ctx);
			modbus_free(ctx);
		}

		if(receive_can_message(can_mosq_fd->can_fd, &received_frame) < 0)
		{
			log_error("can no data was receive\n ");
		}

		// 构建 JSON payload
		index += snprintf(conn->payload + index, sizeof(conn->payload) - index,
				"{\"method\":\"thing.service.property.set\",\"id\":\"10353468\",\"params\":{\"can_id\":%d,\"can_dlc\":%d,",
				(int)received_frame.can_id, (int)received_frame.can_dlc);

		// 添加 can_data 字段到 params
		for (int i = 0; i < received_frame.can_dlc; i++) {
			index += snprintf(conn->payload + index, sizeof(conn->payload) - index,
					"\"can_data%d\":%d,", i, (int)received_frame.data[i]);
		}

		for(int i=0; i<rc; i++)
		{
			if( i==rc-1 )
			{
				index += snprintf(conn->payload + index, sizeof(conn->payload)-index, "\"modbus_noisy\":%d",tab_reg[i]);
				noisy = tab_reg[i];
			}
			else
			{
				index += snprintf(conn->payload + index, sizeof(conn->payload)-index, "\"modbus_lux\":%d,",tab_reg[i]);
				lux = tab_reg[i];

			}
		}
		// add the closing brachet and version field
		index += snprintf(conn->payload + index, sizeof(conn->payload) - index,
				"},\"version\":\"1.0.0\"}");

		float value = ((received_frame.data[0] << 8) | received_frame.data[1]) / 100.0f;
		float value1 = ((received_frame.data[2] << 8) | (received_frame.data[3]))/100.0f;	

		snprintf(buf, sizeof(buf), "%d",lux);   //不能放进读取modbus数据的for里面否则导致连接超时
		lv_label_set_text(ui_Label3, buf);

		snprintf(buf, sizeof(buf), "%d",noisy);
		lv_label_set_text(ui_Label5, buf);

		snprintf(buf, sizeof(buf), "%.2f", value);
		lv_label_set_text(ui_Label8, buf);

		snprintf(buf, sizeof(buf), "%.2f", value1);
		lv_label_set_text(ui_Label9, buf);
		//printf("生成的 JSON payload: %s\n", conn->payload);
		//snprintf(buf,sizeof(buf), "100");
		//lv_label_set_text(ui_Label3, buf);
		//lv_label_set_text(ui_Label5, "100");
		lv_timer_handler();

		pthread_mutex_lock(&mqtt_mutex);
		int ret = mosquitto_publish(can_mosq_fd->mosq, &mid1, conn->pub_topic, strlen(conn->payload), conn->payload, conn->qos, 0);
		pthread_mutex_unlock(&mqtt_mutex);

		if (ret != MOSQ_ERR_SUCCESS) 
		{
			log_error("mosquitto publish data failure: %s\n", strerror(errno));
			continue;
		}
		printf("published message: %s\n", conn->payload);
		sleep(1);
	}

	return NULL;
}


uint32_t custom_tick_get(void)
{
	static uint64_t start_ms = 0;
	if(start_ms == 0) {
		struct timeval tv_start;
		gettimeofday(&tv_start, NULL);
		start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
	}

	struct timeval tv_now;
	gettimeofday(&tv_now, NULL);
	uint64_t now_ms;
	now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;

	uint32_t time_ms = now_ms - start_ms;
	return time_ms;
}

